package cn.microservices.cloud.gateway.filter;

import cn.microservices.cloud.gateway.entity.LogData;
import cn.microservices.cloud.gateway.util.WebFluxUtil;
import cn.microservices.cloud.gateway.exceptions.BusinessException;
import cn.microservices.cloud.gateway.interpector.ip.IpInterceptor;
import cn.microservices.cloud.gateway.interpector.rquest.OnceRequestInterceptor;
import cn.microservices.cloud.gateway.service.AuthService;
import cn.microservices.cloud.gateway.util.FilterRequestResponseUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import cn.microservices.cloud.gateway.util.token.Token;

import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

import static cn.microservices.cloud.gateway.util.FilterRequestResponseUtil.CACHE_REQUEST_BODY_OBJECT_KEY;


//全局过滤器必须实现两个接口：GlobalFilter、Ordered
//GlobalFilter是全局过滤器接口，实现类要实现filter()方法进行功能扩展
//Ordered接口用于排序，通过实现getOrder()方法返回整数代表执行当前过滤器的前后顺序
@Configuration
public class ElapsedFilter implements GlobalFilter, Ordered {

    @Autowired
    private AuthService authService;

    @Autowired
    private IpInterceptor ipInterceptor;

    @Autowired
    private OnceRequestInterceptor onceRequestInterceptor;
    @Value("${spring.profiles.active}")
    private String active;
    private static final Logger logger = LoggerFactory.getLogger(ElapsedFilter.class);

    //起始时间属性名

    private static final String ELAPSED_TIME_BEGIN = "elapsedTimeBegin";

    private static String json = "_json_";
//    private static final String USER_TOKEN_KEY = "metagross_token_list:%s";

    public static void main(String[] args) {
      String rawPath = "/design/client/designSampleIndent/sampleDressNotExamine";
        String pre = rawPath.split("/")[1];
        System.out.println(pre);
    }

    public final static List<String> unAuthTokenList = Arrays.asList("client/ucloud/applyPrivateUrlAuth", "client/ucloud/applyAuth",
        "article/app/mianZeArticle","article/app/beginnersArticle","pay/alipay/payNotify","unauth",
        "pay/wxpay/payNotify","article/app/privacyArticle");

    public final static List unAuthCheckList = Arrays.asList("elment1", "element2");

    //设置为最高优先级，最先执行ElapsedFilter过滤器

    //return Ordered.LOWEST_PRECEDENCE; 代表设置为最低优先级

    @Override
    public int getOrder() {
        //优先让 获取body参数 拦截器 获取body 防止body 无法获取到
        return -1;
//        return Ordered.HIGHEST_PRECEDENCE;

    }


    /**
     * 实现filter()方法记录处理时间
     *
     * @param exchange 用于获取与当前请求、响应相关的数据，以及设置过滤器间传递的上下文数据
     * @param chain    Gateway过滤器链对象
     * @return Mono对应一个异步任务，因为Gateway是基于Netty Server异步处理的,Mono对就代表异步处理完毕的情况。
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        logger.info("网关 入口 ");

        //Pre前置处理部分

        ServerHttpRequest request = exchange.getRequest().mutate()
                .header("from", "gateway").build();
        ServerHttpResponse response = exchange.getResponse();
        String rawPath = request.getURI().getRawPath();
        String prefix = rawPath.split("/")[1];
        //在请求到达时，往ServerWebExchange上下文环境中放入了一个属性elapsedTimeBegin，保存请求执行前的时间戳
        exchange.getAttributes().put(ELAPSED_TIME_BEGIN, System.currentTimeMillis());
        LogData logData = new LogData();
        logData.setCode("0001");
        logData.setMessage("网关日志");
        logData.setUrl(rawPath);
        String ip = WebFluxUtil.getRealIp(request);
        logData.setIp(ip);

        logger.info("网关日志处理 logData = {}", JSONObject.toJSON(logData));

        String timestamp = request.getHeaders().getFirst("timestamp");
        HttpHeaders headers = request.getHeaders();
        logger.info("请求头={}", JSON.toJSON(headers.entrySet()).toString());
        logger.info("请求地址={}", rawPath);

        String token = request.getHeaders().getFirst("token");
        String sign = request.getHeaders().getFirst("sign");
        String equipmentType = request.getHeaders().getFirst("equipmentType");
        String version = request.getHeaders().getFirst("version");
        Object atri =  exchange.getAttributes().get(CACHE_REQUEST_BODY_OBJECT_KEY);
        String bodyStr  = null;
        if (atri!=null){
            Flux<DataBuffer> cachedFlux = (Flux<DataBuffer>) exchange.getAttributes().get(CACHE_REQUEST_BODY_OBJECT_KEY);
            bodyStr  = FilterRequestResponseUtil.resolveBodyFromRequest(cachedFlux);
            logger.info("body={}",bodyStr);
        }
        if (!rawPath.contains("client/ucloud/applyPrivateUrlAuth")
                && !rawPath.contains("client/ucloud/applyAuth")&& !rawPath.contains("article/app/mianZeArticle")
                && !rawPath.contains("article/app/beginnersArticle")
                && !rawPath.contains("pay/alipay/payNotify")
                && !rawPath.contains("unauth")
                && !rawPath.contains("pay/wxpay/payNotify")
                && !rawPath.contains("version/get")
                && !rawPath.contains("client/account/forgetPassword")
                && !rawPath.contains("article/app/privacyArticle")){
            //请求头异常校验
            Boolean headerFlag = false;
            try {
                headerFlag = authService.headersVerification(response,request,token,sign,timestamp,rawPath,bodyStr,equipmentType,version);
            }catch (BusinessException e){
                return response.writeWith(e.getMono());
            }

        }


        //ip拦截
//        if (ipInterceptor.isIllegalByIp(request)) {
//            logger.warn("IP拦截请求失败");
//            byte[] bits = JSONResult.failed(ResultCode.LACK_TIMESTAMP.getCode(),ResultCode.LACK_TIMESTAMP.getValue());
//            DataBuffer buffer = response.bufferFactory().wrap(bits);
//            return response.writeWith(Mono.just(buffer));
//        }

        //一次请求
//        if (onceRequestInterceptor.isInvalid(request)) {
//            logger.warn("一次请求失败");
//            byte[] bits = JSONResult.failed(ResultCode.LACK_TIMESTAMP.getCode(),ResultCode.LACK_TIMESTAMP.getValue());
//            DataBuffer buffer = response.bufferFactory().wrap(bits);
//            return response.writeWith(Mono.just(buffer));
//        }
        if (rawPath.contains("/client/common/login")||rawPath.contains("client/common/sendVerificationCode")
                || rawPath.contains("client/identityRegister/register")
                || rawPath.contains("client/ucloud/applyPrivateUrlAuth")
                || rawPath.contains("client/ucloud/applyAuth")
                || rawPath.contains("article/app/mianZeArticle")
                || rawPath.contains("article/app/beginnersArticle")
                || rawPath.contains("/article/app/privacyArticle")
                || rawPath.contains("pay/wxpay/payNotify")
                || rawPath.contains("pay/alipay/payNotify")
                || rawPath.contains("version/get")
                || rawPath.contains("unauth")
                || rawPath.contains("client/account/forgetPassword")
        ){
            logger.info("无Token 放过接口 url = {}",rawPath);
            return chain.filter(exchange).then(
                    Mono.fromRunnable(() -> { //当前过滤器得到响应时，计算并打印时间
                        Long startTime = exchange.getAttribute(ELAPSED_TIME_BEGIN);
                        if (startTime != null) {
                            logger.info(exchange.getRequest().getRemoteAddress() //远程访问的用户地址
                                    + " | " + exchange.getRequest().getPath() //Gateway URI
                                    + " | cost " + (System.currentTimeMillis() - startTime) + "ms"); //处理时间
                        }

                    })

            );
        }
        Token tk = null;
        try {
            tk = authService.verifyToken(rawPath,token,response);
        }catch (BusinessException e){
            return response.writeWith(e.getMono());
        }
        //获取当前登录用户
//        String loginUserStr = authService.getLoginUser(token,response);

        //设置用户ID
        request = exchange.getRequest().mutate()
                .header("userId", tk.getSubjectId()).build();
        //设置用户身份
        request = exchange.getRequest().mutate()
                .header("identity", tk.getSubjectClass()).build();
        //设置前缀信息
        request = exchange.getRequest().mutate()
                .header("prefix", prefix).build();


        logger.info("userId = {}, userIdentity = {}",tk.getSubjectId(),tk.getSubjectClass());
//        ServerHttpRequest newRequest = request.mutate().path(rawPath).build();
//        ServerWebExchange newExchange = exchange.mutate().request(newRequest).response(response).build();
//        newExchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, newRequest.getURI());
        //chain.filter(exchange).then()对应Post后置处理部分

        //当响应产生后，记录结束与elapsedTimeBegin起始时间比对，获取RESTful API的实际执行时间

        return chain.filter(exchange).then(

                Mono.fromRunnable(() -> { //当前过滤器得到响应时，计算并打印时间

                    Long startTime = exchange.getAttribute(ELAPSED_TIME_BEGIN);

                    if (startTime != null) {

                        logger.info(exchange.getRequest().getRemoteAddress() //远程访问的用户地址

                                + " | " + exchange.getRequest().getPath() //Gateway URI

                                + " | cost " + (System.currentTimeMillis() - startTime) + "ms"); //处理时间

                    }

                })

        );

    }

    /**
     * 从Flux<DataBuffer>中获取字符串的方法
     *
     * @return 请求体
     */
    private String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest) {
        Flux<DataBuffer> body = serverHttpRequest.getBody();
        AtomicReference<String> bodyRef = new AtomicReference<>();
        body.subscribe(dataBuffer -> {
            CharBuffer charBuffer = StandardCharsets.UTF_8.decode(dataBuffer.asByteBuffer());
            DataBufferUtils.release(dataBuffer);
            bodyRef.set(charBuffer.toString());
        });//读取request body到缓存
        String bodyStr = bodyRef.get();//获取request body
        System.out.println(bodyStr);//这里是我们需要做的操作
        return bodyRef.get();
    }
}
